package bootstrap

// 替换gorm的日志输出为lumberjack

import (
	"fmt"
	"go-mp-starter/app/models"
	appCfg "go-mp-starter/config"
	"go-mp-starter/global"
	"io"
	"log"
	"os"
	"time"

	"go.uber.org/zap"
	"gopkg.in/natefinch/lumberjack.v2"
	"gorm.io/driver/mysql"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var dbCfg appCfg.Database

// InitDB 按配置初始化数据库驱动实例
func InitDB() *gorm.DB {
	// 获取加载之后配置
	dbCfg = global.App.Config.Database

	switch dbCfg.Driver {
	// 按配置驱动初始化对应角色
	case "sqlite":
		return initSqliteGorm()
	case "mysql":
		fallthrough
	default:
		return initMysqlGorm()
	}
}

func initSqliteGorm() *gorm.DB {
	db, err := gorm.Open(sqlite.Open(dbCfg.Database+".sqlite"), &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true, // 禁止自动创建外键约束
		Logger:                                   getGormLogger(),
	})

	if err != nil {
		global.App.Log.Error("InitSqlite Fail", zap.Any("err", err))
		return nil
	}
	sqlDb, _ := db.DB()
	sqlDb.SetMaxIdleConns(dbCfg.MaxIdleConns)
	sqlDb.SetMaxOpenConns(dbCfg.MaxOpenConns)
	// 初始化表
	initSqlTables(db)
	return db
}

func initMysqlGorm() *gorm.DB {
	if dbCfg.Database == "" {
		return nil
	}

	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
		dbCfg.UserName, dbCfg.Password, dbCfg.Host, dbCfg.Port, dbCfg.Database, dbCfg.Charset)
	// debug
	// fmt.Printf("DEBUG - mysql_dsn: %s\n", dsn)

	mysqlCfg := mysql.Config{
		DSN:                       dsn,
		DefaultStringSize:         191,   // string类型字段的默认长度
		DisableDatetimePrecision:  true,  // 禁用datetime类型的精度
		DontSupportRenameIndex:    true,  // 重建索引时采用删除后新建的方式; mysql 5.7之前及MariaDB不支持重命名索引
		DontSupportRenameColumn:   true,  // 使用`change`重命名列, mysql8与MariaDB不支持重命名列
		SkipInitializeWithVersion: false, // 根据版本自动配置
	}

	if db, err := gorm.Open(mysql.New(mysqlCfg), &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true, // 禁止自动创建外键约束
		Logger:                                   getGormLogger(),
	}); err != nil {
		global.App.Log.Error("InitMysql Fail", zap.Any("err", err))
		return nil
	} else {
		sqlDB, _ := db.DB()
		sqlDB.SetMaxIdleConns(dbCfg.MaxIdleConns)
		sqlDB.SetMaxOpenConns(dbCfg.MaxOpenConns)
		// 自动初始化数据库表
		initSqlTables(db)
		return db
	}
}

// 自定义gorm writer日志写驱动
func getGromLogWriter() logger.Writer {
	var writer io.Writer
	logCfg := global.App.Config.Log
	if global.App.Config.Database.EnableFileLogWriter {
		writer = &lumberjack.Logger{
			Filename:   logCfg.RootDir + "/" + dbCfg.LogFilename,
			MaxSize:    logCfg.MaxSize,
			MaxBackups: logCfg.MaxBackups,
			MaxAge:     logCfg.MaxAge,
			Compress:   logCfg.Compress,
		}
	} else {
		writer = os.Stdout
	}

	return log.New(writer, "\r\n", log.LstdFlags)
}

// 获取Gorm的日志记录器
func getGormLogger() logger.Interface {
	// 设置日志级别
	var logMode logger.LogLevel
	switch dbCfg.LogMode {
	case "slient":
		logMode = logger.Silent
	case "error":
		logMode = logger.Error
	case "warn":
		logMode = logger.Warn
	case "info":
		logMode = logger.Info
	default:
		logMode = logger.Info
	}
	// 使用自定义的Gorm日志写驱动
	return logger.New(getGromLogWriter(), logger.Config{
		SlowThreshold:             200 * time.Millisecond,     // 慢sql阈值 200ms
		Colorful:                  !dbCfg.EnableFileLogWriter, // 是否启用彩色输出
		IgnoreRecordNotFoundError: false,                      // 是否忽略ErrRecordNotFound(记录未找到) 错误
		LogLevel:                  logMode,                    // 日志级别
	})
}

// initSqlTabels 自动初始化表(自动迁移)
func initSqlTables(db *gorm.DB) {
	err := db.AutoMigrate(
		models.User{},
		models.Media{},
		models.Wxuser{},
	)
	if err != nil {
		global.App.Log.Error("migrate table failed", zap.Any("err", err))
		os.Exit(0)
	}
}
